#!/usr/bin/python3
# -*- coding: utf-8 -*-

"""
# @version: v1.0
# @author : cd
# @Email : 19688513@qq.com
# @Project : horizons-engine-pybroker
# @File : StockSpotDataManager.py
# @Software: PyCharm
# @time: 2025/8/26 19:18
# @description : 实时行情数据管理类
"""

import urllib3
import logging
import pandas as pd
from datetime import datetime, time, timedelta
from typing import Dict, Optional, List, Tuple, Union
import os

from AKShareDataFetcher import AKShareDataFetcher
from SignalGenerator import SignalGenerator
from TradeDateManager import TradeDateManager
from ExcelExporter import ExcelExporter  # 导入Excel导出器
from YearlyStockDataManager import YearlyStockDataManager

# 配置日志
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
# 禁用不安全的请求警告
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
# 股票年度数据数据库路径
STOCK_YEAR_DB_PATH = "data/stock_year_data.db"


class StockSpotDataManager:
    """
    实时行情数据管理类，用于获取和处理沪深京A股实时行情数据
    """

    def __init__(self):
        """
        初始化实时行情数据管理器
        """
        self.data_fetcher = AKShareDataFetcher(
            max_retries=3,
            retry_delay=5,
            request_delay=1
        )
        self.generator = SignalGenerator(max_workers=2)
        self.trade_manager = TradeDateManager()
        self.excel_exporter = ExcelExporter()  # 初始化Excel导出器
        self.spot_data = pd.DataFrame()
        self.last_update_time = None

    def fetch_spot_data(self, force_update: bool = False, days_back: int = 10) -> pd.DataFrame:
        """
        获取沪深京A股实时行情数据，并根据信号数据筛选最新价小于信号收盘价的股票

        同时计算120日成交量均线，并标记出成交量曾超过120日成交量均线的股票

        Args:
            force_update: 是否强制更新数据（忽略缓存）
            days_back: 回溯天数

        Returns:
            pd.DataFrame: 筛选后的实时行情数据
        """
        # 如果数据不是强制更新且已有数据且未过期（5分钟内），则直接返回缓存数据
        if not force_update and not self.spot_data.empty and self.last_update_time:
            elapsed_minutes = (datetime.now() - self.last_update_time).total_seconds() / 60
            if elapsed_minutes < 5:  # 5分钟缓存
                logging.info(f"使用缓存数据，上次更新时间: {self.last_update_time}")
                return self.spot_data.copy()

        # 获取新数据
        try:
            # 获取信号数据
            gsd = self.get_signal_data(days_back)

            # 过滤掉创业板、科创板以及其他特定前缀的股票代码
            gsd = gsd[~gsd['stock_code'].str.startswith(('30', '68', '83', '87', '88', '92'))]

            # 获取实时行情数据
            spot_data = self.data_fetcher.fetch_spot_em()

            # 如果信号数据为空，直接返回所有实时行情数据
            if gsd.empty:
                logging.warning("信号数据为空，返回所有实时行情数据")
                self.spot_data = spot_data
                self.last_update_time = datetime.now()
                return spot_data

            # 根据信号数据筛选实时行情数据
            filtered_data = []

            # 遍历信号数据中的每只股票
            for _, signal_row in gsd.iterrows():
                stock_code = signal_row['stock_code']
                close_price = signal_row['close_price']
                industry = signal_row['industry']
                rating = signal_row['rating']
                signal_date = signal_row['date']

                # 使用 TradeDateManager 计算日期范围
                start_date, end_date = self.trade_manager.get_start_end_date(signal_date, days_back)

                # 在实时行情数据中查找匹配的股票
                spot_row = spot_data[spot_data['代码'] == stock_code]

                # 如果找到匹配的股票且最新价小于信号收盘价
                if not spot_row.empty and spot_row.iloc[0]['最新价'] < close_price:
                    # 查询该股票指定交易日内的信号数据
                    stock_signal_data = self.generator.query_signal_data_all(
                        signal_id=1,
                        start_date=start_date,
                        end_date=end_date,
                        stock_codes=[stock_code]
                    )

                    # 将日期和收盘价拼接成字符串
                    last_signal_date = signal_date  # 默认使用当前信号日期
                    if not stock_signal_data.empty:
                        # 确保有 date 和 close_price 列
                        if 'date' in stock_signal_data.columns and 'close_price' in stock_signal_data.columns:
                            # 按日期排序
                            stock_signal_data = stock_signal_data.sort_values('date')
                            # 获取最后一条信号的日期
                            if not stock_signal_data.empty:
                                last_signal_date = stock_signal_data.iloc[-1]['date']
                            # 拼接日期和收盘价
                            history_str = stock_signal_data.apply(
                                lambda row: f"{row['date']}:{row['close_price']:.2f}", axis=1
                            ).str.cat(sep=';')
                        else:
                            # 如果列名不匹配，记录错误
                            logging.error(f"信号数据列名不匹配: {stock_signal_data.columns.tolist()}")
                            history_str = "数据列名不匹配"
                    else:
                        history_str = "无历史信号数据"

                    # 添加额外的信号信息到实时行情数据
                    spot_row = spot_row.copy()
                    # spot_row['参考价格'] = close_price
                    # spot_row['industry'] = industry
                    # spot_row['rating'] = rating
                    # spot_row['history_signals'] = history_str
                    # spot_row['last_signal_date'] = last_signal_date  # 添加最后信号日期
                    # spot_row['volume_breakout'] = False  # 初始化成交量突破标记
                    spot_row['参考价格'] = close_price
                    spot_row['行业'] = industry  # 改为中文
                    spot_row['评级'] = rating  # 改为中文
                    spot_row['历史信号'] = history_str  # 改为中文
                    spot_row['最后信号日期'] = last_signal_date  # 改为中文
                    spot_row['成交量突破'] = False  # 改为中文

                    filtered_data.append(spot_row)

            # 合并所有筛选后的数据
            if filtered_data:
                self.spot_data = pd.concat(filtered_data, ignore_index=True)

                # 初始化YearlyStockDataManager
                data_manager = YearlyStockDataManager(
                    db_path=STOCK_YEAR_DB_PATH,
                    batch_size=200,
                    cache_size_mb=512
                )

                # 为每只股票获取历史数据并计算120日成交量均线
                for idx, row in self.spot_data.iterrows():
                    stock_code = row['代码']
                    last_signal_date = row['最后信号日期']

                    # 获取120个交易日的历史数据
                    end_date = datetime.now().strftime('%Y%m%d')
                    start_date, _ = self.trade_manager.get_start_end_date(end_date, 120)
                    stock_data = data_manager.get_stock_data(stock_code, start_date, end_date)

                    if not stock_data.empty:
                        # 计算120日成交量均线
                        stock_data['volume_ma120'] = stock_data['volume'].rolling(window=120, min_periods=1).mean()

                        # 找到信号日期之后的数据
                        signal_date_idx = stock_data[stock_data['date'] == last_signal_date].index

                        if not signal_date_idx.empty:
                            signal_idx = signal_date_idx[0]
                            # 检查信号日期之后是否有成交量超过120日成交量均线
                            post_signal_data = stock_data.iloc[signal_idx + 1:]

                            if not post_signal_data.empty:
                                # 检查是否有成交量超过120日成交量均线
                                crossed_above = any(post_signal_data['volume'] > post_signal_data['volume_ma120'])

                                if crossed_above:
                                    # 标记成交量突破
                                    self.spot_data.loc[idx, '成交量突破'] = True
                                    # # 添加历史数据到结果
                                    # self.spot_data.loc[idx, 'history_data'] = stock_data.to_json()
                                else:
                                    self.spot_data.loc[idx, '成交量突破'] = False

                # 记录成交量突破的股票数量
                breakout_count = self.spot_data['成交量突破'].sum()
                logging.info(f"找到 {breakout_count} 只成交量曾超过120日成交量均线的股票")
            else:
                self.spot_data = pd.DataFrame()
                logging.info("没有找到符合条件的股票")

            self.last_update_time = datetime.now()
            logging.info(f"成功获取并筛选实时行情数据，共{len(self.spot_data)}条符合条件的记录")

            return self.spot_data.copy()

        except Exception as e:
            logging.error(f"获取实时行情数据失败: {e}")
            return pd.DataFrame()

    def get_signal_data(self, days_back: int = 10) -> pd.DataFrame:
        """
        获取信号数据

        Args:
            days_back: 回溯天数

        Returns:
            pd.DataFrame: 信号数据
        """
        try:
            current_date = datetime.now().strftime("%Y%m%d")
            start_date, end_date = self.trade_manager.get_start_end_date(current_date, days_back)

            signal_data = self.generator.query_signal_data_latest(
                signal_id=1,
                start_date=start_date,
                end_date=end_date
            )

            return signal_data
        except Exception as e:
            logging.error(f"获取信号数据失败: {e}")
            return pd.DataFrame()

    def export_spot_data_to_excel(self, output_dir: str = "reports") -> str:
        """
        导出实时行情数据到Excel文件

        Args:
            output_dir: 输出目录

        Returns:
            str: Excel文件路径
        """
        # 获取实时行情数据
        spot_data = self.fetch_spot_data()

        # 创建文件名
        filename = f"{self.get_current_time('%Y%m%d_%H%M%S')}_筛选股票实时行情.xlsx"
        filepath = os.path.join(output_dir, filename)
        # 导出数据
        return self.excel_exporter.export_stock_data(spot_data, filepath)

    def get_current_time(self, format_str: str = "%Y-%m-%d %H:%M:%S") -> str:
        """
        获取当前时间

        Args:
            format_str: 时间格式字符串，默认为 "%Y-%m-%d %H:%M:%S"

        Returns:
            str: 格式化后的当前时间字符串
        """
        return datetime.now().strftime(format_str)




if __name__ == "__main__":
    """主函数，演示StockSpotDataManager类的使用"""
    # 创建实时行情数据管理器
    spot_manager = StockSpotDataManager()

    # 3. 导出到Excel
    print("💾 正在导出数据到Excel...")

    excel_path = spot_manager.export_spot_data_to_excel()
    if excel_path:
        print(f"✅ 数据已成功导出到: {excel_path}")
    else:
        print("❌ 导出Excel失败")